home *** CD-ROM | disk | FTP | other *** search
- /* See license.txt for terms of usage */
-
- FBL.ns(function() { with (FBL) {
-
- // ************************************************************************************************
- // Constants
-
- const Cc = Components.classes;
- const Ci = Components.interfaces;
- const nsIWebNavigation = Ci.nsIWebNavigation;
- const nsIWebProgressListener = Ci.nsIWebProgressListener;
- const nsIWebProgress = Ci.nsIWebProgress;
- const nsISupportsWeakReference = Ci.nsISupportsWeakReference;
- const nsISupports = Ci.nsISupports;
- const nsIURI = Ci.nsIURI;
-
- const NOTIFY_STATE_DOCUMENT = nsIWebProgress.NOTIFY_STATE_DOCUMENT;
-
- const STATE_IS_WINDOW = nsIWebProgressListener.STATE_IS_WINDOW;
- const STATE_IS_DOCUMENT = nsIWebProgressListener.STATE_IS_DOCUMENT;
- const STATE_IS_REQUEST = nsIWebProgressListener.STATE_IS_REQUEST;
-
- const STATE_START = nsIWebProgressListener.STATE_START;
- const STATE_STOP = nsIWebProgressListener.STATE_STOP;
- const STATE_TRANSFERRING = nsIWebProgressListener.STATE_TRANSFERRING;
-
- const STOP_ALL = nsIWebNavigation.STOP_ALL;
-
- const dummyURI = "about:layout-dummy-request";
- const aboutBlank = "about:blank";
-
- // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
- const tabBrowser = $("content");
-
- // ************************************************************************************************
- // Globals
-
- var contexts = [];
-
- // ************************************************************************************************
-
- top.TabWatcher = extend(new Firebug.Listener(),
- {
- // Store contexts where they can be accessed externally
- contexts: contexts,
-
- initialize: function()
- {
- if (Firebug.TraceModule)
- Firebug.TraceModule.addListener(TraceListener);
-
- if (tabBrowser)
- tabBrowser.addProgressListener(TabProgressListener, NOTIFY_STATE_DOCUMENT);
-
- httpObserver.addObserver(TabWatcherHttpObserver, "firebug-http-event", false);
- },
-
- destroy: function()
- {
- this.shuttingDown = true;
-
- httpObserver.removeObserver(TabWatcherHttpObserver, "firebug-http-event");
-
- if (tabBrowser)
- {
- tabBrowser.removeProgressListener(TabProgressListener);
-
- var browsers = Firebug.chrome.getBrowsers();
- for (var i = 0; i < browsers.length; ++i)
- {
- var browser = browsers[i];
- this.unwatchTopWindow(browser.contentWindow);
- }
- }
-
- if (Firebug.TraceModule)
- Firebug.TraceModule.removeListener(TraceListener);
- },
-
- // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
- /**
- * Called when tabBrowser browsers get a new location OR when we get a explicit user op to open firebug
- * Attaches to a top-level window. Creates context unless we just re-activated on an existing context
- */
- watchTopWindow: function(win, uri, userCommands)
- {
- if (!win)
- {
- return false;
- }
-
- var selectedBrowser = Firebug.chrome.getCurrentBrowser();
- if (selectedBrowser.cancelNextLoad)
- {
- // We need to cancel this load and try again after a delay... this is used
- // mainly to prevent chaos while when the debugger is active when a page
- // is unloaded
- delete selectedBrowser.cancelNextLoad;
- selectedBrowser.webNavigation.stop(STOP_ALL);
- var url = (uri instanceof nsIURI?uri.spec:uri);
- delayBrowserLoad(selectedBrowser, url);
- return;
- }
-
- var context = this.getContextByWindow(win);
- if (context) // then we've looked at this window before in this FF session...
- {
- if (!this.shouldShowContext(context))
- {
- // ...but now it is not wanted.
- if (context.browser)
- delete context.browser.showFirebug;
- this.unwatchContext(win, context);
-
- // There shouldn't be context for this window so, remove it from the
- // global array.
- remove(contexts, context);
-
- return; // did not create a context
- }
- // else we should show
- }
- else // then we've not looked this window in this session
- {
- // decide whether this window will be debugged or not
- var url = (uri instanceof nsIURI) ? uri.spec : uri;
- if (!this.shouldCreateContext(selectedBrowser, url, userCommands))
- {
- delete selectedBrowser.showFirebug;
- this.watchContext(win, null);
-
- return false; // we did not create a context
- }
-
- var browser = this.getBrowserByWindow(win);
-
- context = this.createContext(win, browser, Firebug.getContextType());
- }
-
- if (win instanceof Ci.nsIDOMWindow && win.parent == win)
- {
- win.addEventListener("pageshow", onLoadWindowContent, onLoadWindowContent.capturing);
- win.addEventListener("DOMContentLoaded", onLoadWindowContent, onLoadWindowContent.capturing);
- }
-
- // Dispatch watchWindow for the outer most DOM window
- this.watchWindow(win, context);
-
- // This is one of two places that loaded is set. The other is in watchLoadedTopWindow
- if (context && !context.loaded)
- {
- context.loaded = !context.browser.webProgress.isLoadingDocument;
-
- // If the loaded flag is set, the proper event should be dispatched.
- if (context.loaded)
- dispatch(this.fbListeners, "loadedContext", [context]);
-
- }
-
- if (context && !context.loaded && !context.showContextTimeout)
- {
- // still loading, we want to showContext one time but not too agressively
- // xxxHonza: In case where the timeout is fired before the context is actually
- // loaded (i.e. context.loaded == false) and the showContext is called
- // some panels (css, html, dome) are not initialized and remain empty.
- // These panels use if (context.loaded) condidtion to execute the init process.
- // I am increasing the timeout to 2000, I guess that in most cases this shouldn't
- // caus any real delay since showContext should be called through
- // watchLoadedTopWindow and this timeout cancelled.
- context.showContextTimeout = setTimeout(bindFixed( function delayShowContext()
- {
- if (context.window) // Sometimes context.window is not defined ?
- this.rushShowContext(win, context); // calls showContext
- else
- {
- }
- }, this), 2000);
- }
- else
- {
- this.rushShowContext(win, context);
- }
-
- return context; // we did create or find a context
- },
-
- rushShowContext: function(win, context)
- {
- if (context.showContextTimeout) // then the timeout even has not run, we'll not need it after all.
- clearTimeout(context.showContextTimeout);
- delete context.showContextTimeout;
-
- // Call showContext only for currently active tab.
- var currentURI = Firebug.chrome.getCurrentURI();
- if (!currentURI || currentURI.spec != context.browser.currentURI.spec)
- {
- return;
- }
-
- this.watchContext(win, context); // calls showContext
- },
-
- // Listeners decide to show or not
- shouldShowContext: function(context)
- {
- if ( dispatch2(this.fbListeners, "shouldShowContext", [context]))
- return true;
- else
- return false;
- },
-
- // Listeners given force-in and veto on URIs/Window.
-
- shouldCreateContext: function(browser, url, userCommands)
- {
- // called when win has no context, answers the question: create one, true or false?
-
- if (!this.fbListeners)
- return userCommands;
-
- // Create if any listener says true to showCreateContext
- if (dispatch2(this.fbListeners, "shouldCreateContext", [browser, url, userCommands]))
- return true;
-
- if (dispatch2(this.fbListeners, "shouldNotCreateContext", [browser, url, userCommands]))
- return false;
-
- return userCommands;
- },
-
- createContext: function(win, browser, contextType)
- {
- if (contexts.length == 0)
- Firebug.broadcast('enableXULWindow', []);
-
- // If the page is reloaded, store the persisted state from the previous
- // page on the new context
- var persistedState = browser.persistedState;
- delete browser.persistedState;
- var location = safeGetWindowLocation(win).toString();
- //if (!persistedState || persistedState.location != location)
- // persistedState = null;
-
- // xxxHonza, xxxJJB: web application detection. Based on domain check.
- var prevDomain = persistedState ? getDomain(persistedState.location) : null;
- var domain = getDomain(location);
- if (!persistedState || prevDomain != domain)
- persistedState = null;
-
- // The proper instance of FirebugChrome object (different for detached Firebug and
- // accessible as Firebug.chrome property) must be used for the context object.
- // (the global context object FirebugContext is also different for detached firebug).
- var context = new contextType(win, browser, Firebug.chrome, persistedState);
- contexts.push(context);
-
- context.uid = FBL.getUniqueId();
-
- browser.showFirebug = true; // this is the only place we should set showFirebug.
-
- dispatch(this.fbListeners, "initContext", [context, persistedState]);
-
- return context;
- },
-
- /**
- * Called once the document within a tab is completely loaded.
- */
- watchLoadedTopWindow: function(win)
- {
- var isSystem = isSystemPage(win);
-
- var context = this.getContextByWindow(win);
- if ((context && !context.window))
- {
- this.unwatchTopWindow(win);
- this.watchContext(win, null, isSystem);
- return;
- }
-
- if (context && !context.loaded)
- {
- context.loaded = true;
-
- dispatch(this.fbListeners, "loadedContext", [context]);
-
- // DOMContentLoaded arrived. Whether or not we did showContext at 400ms, do it now.
- this.rushShowContext(win, context);
- }
- },
-
- /**
- * Attaches to a window that may be either top-level or a frame within the page.
- */
- watchWindow: function(win, context)
- {
- if (!context)
- context = this.getContextByWindow(getRootWindow(win));
-
- var location = safeGetWindowLocation(win);
-
- // Unfortunately, dummy requests that trigger the call to watchWindow
- // are called several times, so we have to avoid dispatching watchWindow
- // more than once
- if (context && context.windows.indexOf(win) == -1 && location != aboutBlank)
- {
- context.windows.push(win);
-
- if (win.parent == win)
- {
- win.addEventListener("pagehide", onPageHideTopWindow, false);
- }
- else
- {
- win.addEventListener("unload", onUnloadWindow, false);
- }
-
- dispatch(this.fbListeners, "watchWindow", [context, win]);
-
- }
- },
-
- /**
- * Detaches from a top-level window. Destroys context
- * Called when windows are closed, or user closes firebug
- */
- unwatchTopWindow: function(win)
- {
- var context = this.getContextByWindow(win);
- this.unwatchContext(win, context);
-
- return true; // we might later allow extensions to reject unwatch
- },
-
- /**
- * Detaches from a window, top-level or frame (interior)
- */
- unwatchWindow: function(win)
- {
- var context = this.getContextByWindow(win);
-
- if (!context)
- {
- return;
- }
-
- var index = context.windows.indexOf(win);
- if (index != -1)
- {
- context.windows.splice(index, 1);
- dispatch(this.fbListeners, "unwatchWindow", [context, win]);
- }
- },
-
- /**
- * Attaches to the window inside a browser because of user-activation
- * returns false if no context was created by the attach attempt, eg extension rejected page
- */
- watchBrowser: function(browser)
- {
- registerFrameListener(browser);
-
- var shouldDispatch = this.watchTopWindow(browser.contentWindow, safeGetURI(browser), true);
-
- if (shouldDispatch)
- {
- dispatch(this.fbListeners, "watchBrowser", [browser]);
- return true;
- }
- return false;
- },
-
- /*
- * User closes Firebug
- */
-
- unwatchBrowser: function(browser, userCommands)
- {
- if (!browser)
- return;
-
- delete browser.showFirebug;
-
- var shouldDispatch = this.unwatchTopWindow(browser.contentWindow);
-
- if (shouldDispatch)
- {
- dispatch(this.fbListeners, "unwatchBrowser", [browser, userCommands]);
- return true;
- }
- return false;
- },
-
- watchContext: function(win, context, isSystem) // called when tabs change in firefox
- {
- if (this.shuttingDown)
- return;
-
- var browser = context ? context.browser : this.getBrowserByWindow(win);
- if (browser)
- browser.isSystemPage = isSystem;
-
- dispatch(this.fbListeners, "showContext", [browser, context]); // context is null if we don't want to debug this browser
- },
-
- unwatchContext: function(win, context)
- {
- if (!context)
- {
- var browser = this.getBrowserByWindow(win);
- if (browser)
- {
- browser.persistedState = {};
- delete browser.showFirebug;
- dispatch(this.fbListeners, "showContext", [browser, null]); // context is null if we don't want to debug this browser
- }
- dispatch(this.fbListeners, "destroyContext", [null, (browser?browser.persistedState:null), browser]);
- return;
- }
-
- var persistedState = {location: context.getWindowLocation()};
- context.browser.persistedState = persistedState; // store our state on FF browser elt
-
- iterateWindows(context.window, function(win)
- {
- dispatch(TabWatcher.fbListeners, "unwatchWindow", [context, win]);
- });
-
- dispatch(this.fbListeners, "destroyContext", [context, persistedState, context.browser]);
-
- if (this.cancelNextLoad)
- {
- delete this.cancelNextLoad;
- context.browser.cancelNextLoad = true;
- }
-
- context.destroy(persistedState);
- remove(contexts, context);
-
- var currentBrowser = Firebug.chrome.getCurrentBrowser();
- if (!currentBrowser.showFirebug) // unwatchContext can be called on an unload event after another tab is selected
- dispatch(this.fbListeners, "showContext", [browser, null]); // context is null if we don't want to debug this browser
-
- if (contexts.length == 0)
- Firebug.broadcast("disableXULWindow", []);
- },
-
- // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
- getContextByWindow: function(winIn)
- {
- if (!winIn)
- return;
-
- var rootWindow = getRootWindow(winIn);
-
- //if (FBTrace.DBG_INITIALIZE)
- // FBTrace.sysout("winIn: "+safeGetWindowLocation(winIn).substr(0,50)+" rootWindow: "+safeGetWindowLocation(rootWindow));
-
- if (rootWindow)
- {
- for (var i = 0; i < contexts.length; ++i)
- {
- var context = contexts[i];
- if (context.window == rootWindow)
- return context;
- }
- }
- },
-
- getContextBySandbox: function(sandbox)
- {
- for (var i = 0; i < contexts.length; ++i)
- {
- var context = contexts[i];
- if (context.sandboxes)
- {
- for (var iframe = 0; iframe < context.sandboxes.length; iframe++)
- {
- if (context.sandboxes[iframe] == sandbox)
- return context;
- }
- }
- }
- return null;
- },
-
- getBrowserByWindow: function(win)
- {
- var browsers = Firebug.chrome.getBrowsers();
- for (var i = 0; i < browsers.length; ++i)
- {
- var browser = browsers[i];
- if (browser.contentWindow == win)
- {
- registerFrameListener(browser);
- return browser;
- }
- }
-
- return null;
- },
-
- iterateContexts: function(fn)
- {
- for (var i = 0; i < contexts.length; ++i)
- {
- var rc = fn(contexts[i]);
- if (rc)
- return rc;
- }
- },
- });
-
- // ************************************************************************************************
-
- var TabProgressListener = extend(BaseProgressListener,
- {
- onLocationChange: function(progress, request, uri)
- {
- // Only watch windows that are their own parent - e.g. not frames
- if (progress.DOMWindow.parent == progress.DOMWindow)
- {
- var srcWindow = getWindowForRequest(request);
- var browser = srcWindow ? TabWatcher.getBrowserByWindow(srcWindow) : null;
- var requestFromFirebuggedWindow = browser && browser.showFirebug;
-
- if (uri && uri.scheme === "wyciwyg") // document.open() was called, the document was cleared.
- evictTopWindow(progress.DOMWindow, uri);
-
- if (uri)
- TabWatcher.watchTopWindow(progress.DOMWindow, uri);
- else // the location change to a non-uri means we need to hide
- TabWatcher.watchContext(progress.DOMWindow, null, true);
- }
- },
-
- onStateChange: function(progress, request, flag, status)
- {
- }
- });
-
- // ************************************************************************************************
-
- var FrameProgressListener = extend(BaseProgressListener,
- {
- onStateChange: function(progress, request, flag, status)
- {
- if (flag & STATE_IS_REQUEST && flag & STATE_START)
- {
- // We need to get the hook in as soon as the new DOMWindow is created, but before
- // it starts executing any scripts in the page. After lengthy analysis, it seems
- // that the start of these "dummy" requests is the only state that works.
-
- var safeName = safeGetName(request);
- if (safeName && ((safeName == dummyURI) || safeName == "about:document-onload-blocker") )
- {
- var win = progress.DOMWindow;
- // Another weird edge case here - when opening a new tab with about:blank,
- // "unload" is dispatched to the document, but onLocationChange is not called
- // again, so we have to call watchTopWindow here
-
- if (win.parent == win && (win.location.href == "about:blank"))
- {
- TabWatcher.watchTopWindow(win, win.location.href);
- return;
- }
- else
- TabWatcher.watchWindow(win);
- }
- }
-
- // Later I discovered that XHTML documents don't dispatch the dummy requests, so this
- // is our best shot here at hooking them.
- if (flag & STATE_IS_DOCUMENT && flag & STATE_TRANSFERRING)
- {
- TabWatcher.watchWindow(progress.DOMWindow);
- return;
- }
-
- }
- });
-
- // Registers frame listener for specified tab browser.
- function registerFrameListener(browser)
- {
- if (browser.frameListener)
- return;
-
- browser.frameListener = FrameProgressListener; // just a mark saying we've registered. TODO remove!
- browser.addProgressListener(FrameProgressListener, NOTIFY_STATE_DOCUMENT);
-
- }
-
- function getRefererHeader(request)
- {
- var http = QI(request, Ci.nsIHttpChannel);
- var referer = null;
- http.visitRequestHeaders({
- visitHeader: function(name, value)
- {
- if (name == 'referer')
- referer = value;
- }
- });
- return referer;
- }
-
- var TabWatcherHttpObserver = extend(Object,
- {
- // nsIObserver
- observe: function(aSubject, aTopic, aData)
- {
- try
- {
- if (aTopic == "http-on-modify-request")
- {
- aSubject = aSubject.QueryInterface(Ci.nsIHttpChannel);
- this.onModifyRequest(aSubject);
- }
- }
- catch (err)
- {
- ERROR(err);
- }
- },
-
- onModifyRequest: function(request)
- {
- var win = getWindowForRequest(request);
- var tabId = Firebug.getTabIdForWindow(win);
-
- // Tab watcher is only interested in tab related requests.
- if (!tabId)
- return;
-
- // Ignore redirects
- if (request.URI.spec != request.originalURI.spec)
- return;
-
- // A document request for the specified tab is here. It can be a top window
- // request (win == win.parent) or embedded iframe request.
- if (request.loadFlags & Ci.nsIHttpChannel.LOAD_DOCUMENT_URI)
- {
- if ( (FBTrace.DBG_ACTIVATION || FBTrace.DBG_WINDOWS) && win == win.parent)
- {
- FBTrace.sysout("-> tabWatcher TabWatcherHttpObserver *** START *** " +
- "document request for: " + request.URI.spec + " window for request is "+safeGetWindowLocation(win)+"\n");
- }
-
- if (win == win.parent)
- {
- // Make sure the frame listener is registered for top level window so,
- // we can get all onStateChange events and init context for all opened tabs.
- var browser = TabWatcher.getBrowserByWindow(win);
-
- if (!browser)
- return;
-
- delete browser.FirebugLink;
-
- if (safeGetWindowLocation(win).toString() == "about:blank") // then this page is opened in new tab or window
- {
- var referer = getRefererHeader(request);
- if (referer)
- {
- try
- {
- var srcURI = makeURI(referer);
- browser.FirebugLink = {src: srcURI, dst: request.URI};
- }
- catch(e)
- {
- }
- }
- }
- else
- {
- // Here we know the source of the request is 'win'. For viral activation and web app tracking
- browser.FirebugLink = {src: browser.currentURI, dst: request.URI};
- }
- }
- }
- },
-
- QueryInterface : function (aIID)
- {
- if (aIID.equals(Ci.nsIObserver) ||
- aIID.equals(Ci.nsISupportsWeakReference) ||
- aIID.equals(Ci.nsISupports))
- {
- return this;
- }
-
- throw Components.results.NS_NOINTERFACE;
- }
- });
-
- // ************************************************************************************************
- // Local Helpers
-
- function onPageHideTopWindow(event)
- {
- var win = event.currentTarget; // we set the handler on a window
- var doc = event.target; // the pagehide is sent to the document.
- if (doc.defaultView != win)
- return; // ignore page hides on interior windows
-
- win.removeEventListener("pagehide", onPageHideTopWindow, false);
-
- // http://developer.mozilla.org/en/docs/Using_Firefox_1.5_caching#pagehide_event
- if (event.persisted) // then the page is cached and there cannot be an unload handler
- {
- // see Bug 484710 - add pageIgnore event for pages that are ejected from the bfcache
-
- TabWatcher.unwatchTopWindow(win);
- }
- else
- {
- // Page is not cached, there may be an unload
- win.addEventListener("unload", onUnloadTopWindow, true);
- }
- }
-
- function evictTopWindow(win, uri)
- {
- TabWatcher.unwatchTopWindow(win);
- }
-
- function onUnloadTopWindow(event)
- {
- var win = event.currentTarget;
- win.removeEventListener("unload", onUnloadTopWindow, true);
- TabWatcher.unwatchTopWindow(win);
- }
-
- function onLoadWindowContent(event)
- {
- var win = event.currentTarget;
- try
- {
- win.removeEventListener("pageshow", onLoadWindowContent, onLoadWindowContent.capturing);
- }
- catch (exc)
- {
- }
-
- try
- {
- win.removeEventListener("DOMContentLoaded", onLoadWindowContent, onLoadWindowContent.capturing);
- }
- catch (exc)
- {
- }
-
- // Signal that we got the onLoadWindowContent event. This prevents the FrameProgressListener from sending it.
- var context = TabWatcher.getContextByWindow(win);
- if (context)
- context.onLoadWindowContent = true;
-
- try
- {
- TabWatcher.watchLoadedTopWindow(win);
- }
- catch(exc)
- {
- }
-
- }
- onLoadWindowContent.capturing = false;
-
- function onUnloadWindow(event)
- {
- var win = event.currentTarget;
- var eventType = "unload";
- win.removeEventListener(eventType, onUnloadWindow, false);
- TabWatcher.unwatchWindow(win);
- }
-
- function delayBrowserLoad(browser, uri)
- {
- setTimeout(function delayBrowserLoad100()
- {
- browser.loadURI(uri);
- }, 100);
- }
-
- function safeGetName(request)
- {
- try
- {
- return request.name;
- }
- catch (exc)
- {
- return null;
- }
- }
-
- function safeGetURI(browser)
- {
- try
- {
- return browser.currentURI;
- }
- catch (exc)
- {
- return null;
- }
- }
-
- // ************************************************************************************************
-
- var TraceListener =
- {
- onDump: function(message)
- {
- var prefix = "->";
- if (message.text.indexOf(prefix) == 0)
- {
- message.text = message.text.substr(prefix.length);
- message.text = trim(message.text);
- message.type = "DBG_WINDOWS";
- }
- }
- };
-
- // ************************************************************************************************
-
- }});
-